/**
*    Copyright (C) 2008  Chase Kernan 
*    chase.kernan@gmail.com
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
* 
**/

package com.chasekernan.hxnova.server;

import com.chasekernan.hxnova.core.dataholders.Global;
import com.chasekernan.hxnova.core.dataholders.Map;
import com.chasekernan.hxnova.core.dataholders.MessageHandler;
import com.chasekernan.hxnova.core.dataholders.Players;
import com.chasekernan.hxnova.core.dataholders.SalvageHolder;
import com.chasekernan.hxnova.core.dataholders.StarsHolder;
import com.chasekernan.hxnova.core.fleets.Fleet;
import com.chasekernan.hxnova.core.dataholders.FleetHolder;
import com.chasekernan.hxnova.core.players.PlayerTurnFile;
import com.chasekernan.hxnova.utils.Utils;
import com.chasekernan.hxnova.utils.Timing;
import com.chasekernan.hxnova.core.players.Player;

/**
    All new host information for a turn is dumped into this file, which is
    later saved and saved to the server.
**/
class HostTurnFile {
    
    public var year : Int;
    public var scores : Array<Int>;
    public var ranks: Array<Int>;
    public var stars : StarsHolder;
    public var fleets : FleetHolder;
    public var players : Players;
    public var messages : MessageHandler;
    public var salvage : SalvageHolder;
    public var playersMissingTurn : Array<Player>;
    public var playerData : Array<PlayerTurnFile>;
    public var game : Game;
    
    /**
        Creates a new turn file for player [p] and uses the year specified in
        [Global].
    **/
    public function new() {
        year = Global.year;
        
        stars = new StarsHolder();
        //fleets = new FleetHolder();
        
        scores = new Array();
        ranks = new Array();
        
        players = new Players();
        //messages = new MessageHandler();
        
        playersMissingTurn = new Array();
        playerData = new Array();
        
        salvage = new SalvageHolder();
        game = new Game();
    }
    
    /**
        Signifies that a player missed a turn and had no orders.
        If a player is added twice, no error will be thrown and the method just
        returns.
    **/
    public function addPlayerTurnMissed(p : Player) {
        for (p2 in playersMissingTurn) {
            if (p2.id == p.id) return;
        }
        playersMissingTurn.push(p);
    }
    
    /**
        Returns true if the player missed a turn the year of this file.
    **/
    public function getPlayerMissedTurn(p : Player) : Bool {
        for (p2 in playersMissingTurn) {
            if (p2.id == p.id) return true;
        }
        return false;
    }
    
    public static var XML_TITLE = "host_turn_file";
    
    /**
        Writes all the host data of a turn to xml.
        Sample xml:
            TODO: run [writeToXml]
    **/
    public static function writeToXml(htf : HostTurnFile) : Xml {
        if (htf == null) throw "Tried to write null host turn file.";
        
        var xml = Xml.parse('<' + XML_TITLE + '></' + XML_TITLE + '>')
                .firstChild();
                
        xml.set('year', Std.string(htf.year));
        xml.set('num_players', Std.string(htf.players.players.length));
        
        //need to do it this way due to errors in haxe xml.
        var scores = Xml.parse('<scores></scores>').firstElement();
        scores.firstChild().nodeValue = htf.scores.join(',');
        xml.addChild(scores);
        
        var ranks = Xml.parse('<ranks></ranks>').firstElement();
        ranks.firstChild().nodeValue = htf.ranks.join(',');
        xml.addChild(ranks);
        
        //save players missing this turn.
        var missingTurnIDs = new Array<Int>();
        for (p in htf.playersMissingTurn) missingTurnIDs.push(p.id);
        
        var missingTurnXml = Xml.parse("<missing_turns></missing_turns>")
                .firstChild();
        missingTurnXml.firstChild().nodeValue = missingTurnIDs.join(",");
        xml.addChild(missingTurnXml);
        
        xml.addChild(Players.writeToXml(htf.players));
        
        xml.addChild(StarsHolder.writeToXml(htf.stars));
        
        xml.addChild(FleetHolder.writeToXml(htf.fleets));
        
        var allMessages = Xml.createElement("all_messages");
        for (p in htf.players.players) {
            allMessages.addChild(MessageHandler.writePlayerMessagesToXml(p));
            allMessages.addChild(Xml.createElement("spacer"));
        }
        xml.addChild(allMessages);
        
        var playerDataXml = Xml.createElement("player_data");
        for (pd in htf.playerData) {
            playerDataXml.addChild(PlayerTurnFile.writeToXml(pd));
        }
        xml.addChild(playerDataXml);
        
        xml.addChild(SalvageHolder.writeToXml(htf.salvage));
        xml.addChild(Game.writeToXml(htf.game));
        
        return xml;
    }
    
    /**
        NOTE: This loads the data into Global.
    **/
    public static function readFromXml(xml : Xml) : HostTurnFile {
        try {
            Timing.clear();
            var htf = new HostTurnFile();
            htf.year = Std.parseInt(xml.get("year"));
            Timing.record("host turn file init");
            
            //Global.stars and Global.players depend on eachother so a little
            //bit of manipulation is required to load them separately
            var mapString = IO.uncompress(IO.readFromFile( 
                    Server.gameName + "/" + Server.gameName + "." + 
                    Server.MAP_EXTENSION));
            var map = Map.readFromXml(Xml.parse(mapString).firstChild());
            Global.stars = htf.stars = new StarsHolder(map.stars);
            Timing.record("host turn file map");
            
            var numPlayers = Std.parseInt(xml.get("num_players"));
            Global.players = new Players();
            for (i in 0...numPlayers) Global.players.addPlayer(new Player());
            
            htf.players = Players.readFromXml(Utils.getXmlElement(xml, 
                    Players.XML_TITLE));
            Global.players = htf.players;
            Timing.record("host turn file players");
            
            htf.stars = StarsHolder.readFromXml(Utils.getXmlElement(xml, 
                    StarsHolder.XML_TITLE));
            Global.stars = htf.stars;
            Timing.record("host turn file stars");
            
            //update the star references
            for (player in Global.players) {
                player.homeworld = Global.stars.getStarById(player.homeworld.id);
            }

            //read missing turns (needs to be after parsing players so that
            //players can be accessed by id.
            for (id in Utils.getXmlElement(xml, 'missing_turns').firstChild()
                    .nodeValue.split(',')) {
                htf.playersMissingTurn.push(htf.players.getPlayerById(
                        Std.parseInt(id)));
            }
            
            //read scores
            var scores = Utils.getXmlElement(xml, 'scores').firstChild()
                    .nodeValue.split(',');
            for (score in scores) htf.scores.push(Std.parseInt(score));        
            
            //read ranks
            var ranks = Utils.getXmlElement(xml, 'ranks').firstChild()
                    .nodeValue.split(',');
            for (rank in ranks) htf.ranks.push(Std.parseInt(rank)); 
            Timing.record("host turn file other");
            
            htf.fleets = FleetHolder.readFromXml(Utils.getXmlElement(xml, 
                    FleetHolder.XML_TITLE));
            Global.fleets = htf.fleets;
            Timing.record("host turn file fleets");
            
            var allMessages = Utils.getXmlElement(xml, 'all_messages');
            Global.messages = new MessageHandler();
            htf.messages = Global.messages;
            for (e in allMessages.elementsNamed(MessageHandler.XML_TITLE)) {
                //automatically saves the messages to the host file
                MessageHandler.readPlayerMessagesFromXmlToList(e,  
                        htf.messages);
            }
            Timing.record("host turn file messages");
            
            var playerData = Utils.getXmlElement(xml, 'player_data');
            for (e in playerData.elementsNamed(PlayerTurnFile.XML_TITLE)) {
                htf.playerData.push(PlayerTurnFile.readFromXml(e));
            }
            Timing.record("host turn file player data");
            
            htf.salvage = SalvageHolder.readFromXml(Utils.getXmlElement(xml, 
                    SalvageHolder.XML_TITLE)); 
            Timing.record("host turn file salvage");
            
            htf.game = Game.readFromXml(Utils.getXmlElement(xml,
                    Game.XML_TITLE));
                    
            return htf;
        } catch (e : Dynamic) {
            throw "Error while reading from host turn file: " + e;
        }
    }  
}